package org.opens.javaskill.design.apply;

import cn.hutool.core.collection.CollectionUtil;

import java.util.Arrays;
import java.util.List;

/**
 * @Date 2020/9/2 20:23
 * @Created by 99126
 */
public class ObserverPatternForThreadApply {

    public static void main(String[] args) {
        ThreadLifeCycleObserver observer = new ThreadLifeCycleObserver();
        observer.concurrentQuery(Arrays.asList("1", "2"));
    }

}

abstract class ObserverRunnable implements Runnable {

    protected final LifeCycleListener listener;

    public ObserverRunnable(LifeCycleListener listener) {
        this.listener = listener;
    }

    public void notifyChange(final RunnableEvent event) {
        listener.onEvent(event);
    }

}

enum RunnableState {
    RUNNING, ERROR, DONE;
}

class RunnableEvent {

    private final RunnableState state;

    private final Thread thread;

    private final Throwable throwable;

    public RunnableEvent(RunnableState state, Thread thread, Throwable throwable) {
        this.state = state;
        this.thread = thread;
        this.throwable = throwable;
    }

    public RunnableState getState() {
        return state;
    }

    public Thread getThread() {
        return thread;
    }

    public Throwable getThrowable() {
        return throwable;
    }
}

interface LifeCycleListener {

    public void onEvent(RunnableEvent event);

}

class ThreadLifeCycleObserver implements LifeCycleListener {

    private static final Object LOCK = new Object();

    public void concurrentQuery(List<String> ids) {
        if ( CollectionUtil.isEmpty(ids) ) {
            return;
        }
        ids.forEach(id -> {
            new Thread(new ObserverRunnable(this) {
                @Override
                public void run() {
                    try {
                        notifyChange(new RunnableEvent(RunnableState.RUNNING, Thread.currentThread(), null));
                        System.out.println("query for " + id);
                        Thread.sleep(3000);
                        notifyChange(new RunnableEvent(RunnableState.DONE, Thread.currentThread(), null));
                    } catch (Exception e) {
                        e.printStackTrace();
                        notifyChange(new RunnableEvent(RunnableState.ERROR, Thread.currentThread(), e));
                    }
                }
            }, id).start();
        });
    }

    @Override
    public void onEvent(RunnableEvent event) {
        synchronized (LOCK) {
            System.out.println("the runnable " + event.getThread().getName() + " state to " + event.getState());
            if ( event.getThrowable() != null ) {
                System.out.println("thr runnable " + event.getThread().getName() + " fail");
                event.getThrowable().printStackTrace();
            }
        }
    }

}
